package me.sunpeng.atomics;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

/**
 * @author sp
 */
public class AtomicIntegerFieldUpdaterDemo {
    public static void main(String[] args) throws InterruptedException {
        OldBankAccount oldBankAccount = new OldBankAccount();
        NewBankAccount newBankAccount = new NewBankAccount();
        CountDownLatch countDownLatch = new CountDownLatch(10);

        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                try {
                    for (int j = 0; j < 1000; j++) {
                        oldBankAccount.add();
                        newBankAccount.transMoney(newBankAccount);
                    }
                } finally {
                    countDownLatch.countDown();
                }
            }, String.valueOf(i)).start();
        }

        countDownLatch.await();
        System.out.println(Thread.currentThread().getName() + "\t" + "oldBankAccount  result:" + oldBankAccount.money);
        System.out.println(Thread.currentThread().getName() + "\t" + "newBankAccount  result:" + newBankAccount.money);

    }
}

/**
 * before
 */
class OldBankAccount {
    String bankName = "CCB";
    String bankNo = "1102090212312";
    String owner = "z3";
    int money;

    public synchronized void add() {
        money++;
    }

}

/**
 * after
 * 以一种线程安全的方式操作非线程安全对象的某些字段
 * <p>
 * 需求：
 * 10个线程
 * 每个线程转账1000
 * 不使用synchronized,尝试使用AtomicIntegerFieldUpdater实现
 */
class NewBankAccount {
    String bankName = "CCB";
    String bankNo = "1102090212312";
    String owner = "z3";

    public volatile int money;
    //更新的对象属性必须使用public volatile修饰符
    // 因为对象的属性修改类型原子类都是抽象类，所以每次使用都必须使用静态方法newUpdater()创建一个更新器，并且需要设置想要更新的类和属性
    AtomicIntegerFieldUpdater<NewBankAccount> fieldUpdater =
            AtomicIntegerFieldUpdater.newUpdater(NewBankAccount.class, "money");

    //不加synchronized，保证高性能原子性，局部微创小手术
    public void transMoney(NewBankAccount newBankAccount) {
        fieldUpdater.getAndIncrement(newBankAccount);
    }

}

